#include "TcpServer.h"
#include "EventLoop.h"
#include "Logger.h"
#include "EventLoopThreadPool.h"
#include "EventLoopThread.h"
#include "InetAddress.h"
#include "TcpConnection.h"

#include <strings.h>
#include <string>

EventLoop* CheckLoopNotNull(EventLoop* loop) 
{
  if (loop == nullptr) {
    LOG_FATAL("%s:%s:%d mainLoop is null!\n", __FILE__, __FUNCTION__, __LINE__);
  }
  return loop;
}

TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr, const std::string& nameArg,
           Option option)
  : m_loop(CheckLoopNotNull(loop))
  , m_ipPort(listenAddr.toIpPort())
  , m_name(nameArg)
  , m_acceptor(new Acceptor(loop, listenAddr, option == kReusePort))
  , m_pool(new EventLoopThreadPool(loop, 10, nameArg))
  , m_connectionCallback()
  , m_messageCallback()
  , m_nextConnId(1)
  , m_started(0)
{
  m_acceptor->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}

TcpServer::~TcpServer()
{
  for (auto& item : m_connections) {
    TcpConnectionPtr conn = item.second;
    item.second.reset();
    conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
  }
}

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
  LOG_INFO("New connection from %s\n", peerAddr.toIpPort().c_str());
  // 轮询算法，选择一个 subloop
  EventLoop* ioLoop = m_pool->getNextLoop();
  char buf[64] = {0};
  snprintf(buf, sizeof(buf), "-%s%d", m_ipPort.c_str(), m_nextConnId++);
  sockaddr_in local;
  ::bzero(&local, sizeof(local));
  socklen_t len = sizeof(local);
  if (0 > ::getsockname(sockfd, (sockaddr*)&local, &len)) {
    LOG_ERROR("get sock name failed");
  }
  InetAddress localAddr(local);
  std::string connName = m_name + buf;
  LOG_INFO("New connection [%s] from %s\n", connName.c_str(), peerAddr.toIpPort().c_str());
  TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
  m_connections[connName] = conn;
  conn->setConnectionCallback(m_connectionCallback);
  conn->setMessageCallback(m_messageCallback);
  conn->setWriteCompleteCallback(m_writeCompleteCallback);
  conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
  ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::setThreadNum(int numThreads)
{
  m_pool->setThreadNum(numThreads);

}

void TcpServer::start()
{
  if (m_started == 0) {
    m_started++;
    m_pool->start(m_threadInitCallback);
    m_loop->runInLoop(std::bind(&Acceptor::listen, m_acceptor.get()));
  }
}

void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
  m_loop->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
  LOG_INFO("TcpServer::removeConnection [%s] from %s\n", conn->name().c_str(), conn->peerAddress().toIpPort().c_str());
  m_connections.erase(conn->name());
  // 关闭连接，并释放相关资源
  EventLoop* ioLoop = conn->getLoop();
  ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}